fix: use missing_ok for temp file cleanup to avoid masking errors - #3803
Open
Quratulain-bilal wants to merge 3 commits into
Open
fix: use missing_ok for temp file cleanup to avoid masking errors#3803Quratulain-bilal wants to merge 3 commits into
Quratulain-bilal wants to merge 3 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Updates temporary-file cleanup and also adds nested plan discovery for agent context.
Changes:
- Uses
Path.unlink(missing_ok=True)for temporary files. - Recursively discovers nested plans with symlink containment checks.
- Adds Bash/PowerShell parity tests.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/shared_infra.py |
Updates temporary-file cleanup. |
src/specify_cli/integrations/manifest.py |
Updates manifest cleanup. |
src/specify_cli/_utils.py |
Updates JSON-write cleanup. |
extensions/agent-context/scripts/python/update_agent_context.py |
Adds recursive plan discovery. |
tests/extensions/test_update_agent_context_python_parity.py |
Adds nested-plan parity tests. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 5/5 changed files
- Comments generated: 5
- Review effort level: Medium
| finally: | ||
| if temp_path.exists(): | ||
| temp_path.unlink() | ||
| temp_path.unlink(missing_ok=True) |
| finally: | ||
| if temp_path.exists(): | ||
| temp_path.unlink() | ||
| temp_path.unlink(missing_ok=True) |
Comment on lines
+182
to
+183
| if temp_path: | ||
| temp_path.unlink(missing_ok=True) |
Quratulain-bilal
force-pushed
the
fix/temp-file-unlink-safety
branch
from
July 28, 2026 22:40
117af4b to
868aa89
Compare
Contributor
There was a problem hiding this comment.
Review details
Comments suppressed due to low confidence (8)
src/specify_cli/shared_infra.py:265
missing_ok=Truesuppresses onlyFileNotFoundError; another cleanup failure (for example,PermissionError) will still replace the exception raised by the write/replace operation. Since thisfinallyblock is intended not to mask that failure, suppressOSErroraround the best-effort cleanup.
temp_path.unlink(missing_ok=True)
src/specify_cli/integrations/manifest.py:442
missing_ok=Truehandles a vanished temporary file, but it still lets every otherOSErrorfrom cleanup mask the exception that entered thisfinallyblock. Make removal best-effort by suppressingOSErrorso callers receive the original manifest-save failure.
temp_path.unlink(missing_ok=True)
src/specify_cli/_utils.py:183
- If unlinking fails for any reason other than the file being absent, this cleanup still replaces the original exception despite the PR's stated goal. Catch
OSErrorfrom this best-effort removal before re-raising the original failure.
if temp_path:
temp_path.unlink(missing_ok=True)
tests/extensions/test_update_agent_context_python_parity.py:393
- This fixture symlinks a directory, but
Path.rglob()does not recurse through directory symlinks by default. The test therefore passes even without_resolved_rel()and does not cover the newly added containment check; symlink the discoveredplan.mdfile itself to the outside file instead.
specs = repo / "specs"
specs.mkdir(parents=True, exist_ok=True)
(specs / "linked").symlink_to(outside.parent, target_is_directory=True)
# Sanity: the plan really is reachable through the symlink.
assert (specs / "linked" / "001-x" / "plan.md").is_file()
tests/extensions/test_update_agent_context_python_parity.py:556
- The removed Python code already used recursive
rglob("plan.md"), so the claim that this PR fixes a one-level Python scan is inaccurate. Reword this as an ongoing parity check rather than attributing behavior to the old implementation.
With no feature.json, discovery falls back to scanning under specs/. A plan
at specs/<scope>/<feature>/plan.md sits more than one level deep; the old
Python one-level glob missed it while PowerShell already recurses (#3024).
extensions/agent-context/scripts/python/update_agent_context.py:186
- This resolved-path filtering and its parity tests are outside the PR's stated temporary-file-cleanup scope. Please either move the agent-context behavior change to a dedicated PR or update the PR title/description to explain and justify the additional behavior.
def _resolved_rel(p: Path) -> Path | None:
# Resolve symlinks before checking containment: relative_to() is
# lexical and would otherwise accept a plan reached through a specs/
# symlink that points outside the project, emitting an
# in-project-looking path for an out-of-project file (or picking it
# as "most recent").
try:
return p.resolve().relative_to(root)
except (OSError, ValueError):
tests/extensions/test_update_agent_context_python_parity.py:355
- This describes a one-level Python glob, but the removed implementation already used
(root / "specs").rglob("plan.md"). Please avoid presenting this as a regression fixed by this PR; the implementation change here adds resolved-path containment filtering instead.
This issue also appears on line 554 of the same file.
A plan created under specs/<scope>/<feature>/plan.md (as produced via
SPECIFY_FEATURE_DIRECTORY) is more than one level below specs/. The old
Python port used a one-level specs/*/plan.md glob and missed it, while the
bash/PowerShell twins recurse (#3024). This locks in the parity.
src/specify_cli/commands/bundle/init.py:883
- The PR description limits the change to temporary-file cleanup in three files, but this adds a new 50 MiB behavior limit to remote bundle downloads. Either split this functional/security change into its own PR with focused bundle-level coverage, or update the title and description so reviewers and release notes capture it.
raw = read_response_limited(resp, max_bytes=MAX_DOWNLOAD_BYTES, error_type=BundlerError)
- Files reviewed: 6/6 changed files
- Comments generated: 0 new
- Review effort level: Medium
The Python port's mtime fallback discovered plans with a one-level specs/*/plan.md glob, so a scoped layout created via SPECIFY_FEATURE_DIRECTORY (specs/<scope>/<feature>/plan.md) was missed when feature.json is absent — the fallback returned no plan and the managed context section omitted the 'at <plan>' line. The bash and PowerShell twins were already fixed to recurse (github#3024); the Python twin was left behind. Switch to specs.rglob('plan.md') with the same symlink-safe containment check the bash twin uses (resolve each candidate and confirm it stays within the project root before ranking by mtime), so a plan reached through a specs/ symlink pointing outside the project is not selected. Adds parity regression tests (vs bash and vs PowerShell) covering a nested specs/<scope>/<feature>/plan.md; both fail on the pre-fix one-level glob. Fixes github#3733
The recursive fallback resolves each candidate before the relative_to() containment check, but nothing exercised that path. Add a parity test for a plan reachable only through a specs/ symlink pointing outside the project: relative_to() is lexical and would accept it, emitting an in-project-looking path for an out-of-project file. Both the bash twin and the Python port skip it, so the "at <plan>" line is omitted. Also correct the module docstring, which still described the fallback as scanning specs/*/plan.md one level deep.
Quratulain-bilal
force-pushed
the
fix/temp-file-unlink-safety
branch
from
July 29, 2026 16:01
868aa89 to
1adf9bf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replace if temp_path.exists(): temp_path.unlink() with emp_path.unlink(missing_ok=True) in 3 files: integrations/manifest.py, shared_infra.py, _utils.py. The old pattern could raise OSError if unlink() fails, masking the original exception in inally/except blocks.